/*
 * Copyright (C) 2021 HiHope Open Source Organization .
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 *
 * limitations under the License.
 */
#include <stdio.h>
#include <unistd.h>

#include "ohos_init.h"
#include "cmsis_os2.h"
#include "iot_errno.h"
#include "iot_gpio.h"
#include "iot_uart.h"
#include "hi_io.h"
#include "hi_pwm.h"

#include "pzcar_wheel_ctrl.h"

extern char* eCm_String[];
extern char* eSt_String[];
extern PzCarInfo g_carInfo;
extern unsigned char page_curr;

#define IOT_PWM_PORT_PWM2   2
#define IOT_PWM_BEEP_GPIO   HI_IO_NAME_GPIO_5
#define IOT_PWM_BEEP_DUTY   50
#define IOT_PWM_BEEP_FREQ   4000

#define IOT_UART_IDX_1   (1)  // UNI -> Hi3861
#define UNI_CMD_LEN_MIN  (6)
#define UNI_CMD_LEN_MAX  (8)

// 你好大师兄: 5A A5 64 73 78 5A
unsigned char UNI_wakeup_uni[] = {0x5A, 0xA5, 0x64, 0x73, 0x78, 0x5A};
// 打开报警: 5A A5 04 64 6b 62 6a 5A
unsigned char UNI_TurnonAlarms[]  = {0x5A, 0xA5, 0x04, 0x64, 0x6b, 0x62, 0x6a, 0x5A};
// 关闭报警: 5A A5 04 67 62 62 6a 5A
unsigned char UNI_TurnoffAlarms[] = {0x5A, 0xA5, 0x04, 0x67, 0x62, 0x62, 0x6a, 0x5A};
// 打开台灯|请开灯|开灯|打开灯|我回来了: 5A A5 02 6F 6E 5A
unsigned char UNI_TurnOn[]     = {0x5A, 0xA5, 0x02, 0x6F, 0x6E, 0x5A};
// 关闭台灯|请关灯|关灯|睡觉了|关上灯|我出去了: 5A A5 03 6F 66 66 5A
unsigned char UNI_TurnOff[]    = {0x5A, 0xA5, 0x03, 0x6F, 0x66, 0x66, 0x5A};

// 前进
unsigned char UNI_Forward[]    = {0x5A, 0xA5, 0x02, 0x71, 0x6A, 0x5A};
// 停止
unsigned char UNI_Stop[]       = {0x5A, 0xA5, 0x02, 0x74, 0x7A, 0x5A};
// 后退
unsigned char UNI_Backward[]   = {0x5A, 0xA5, 0x02, 0x68, 0x74, 0x5A};
// 左转
unsigned char UNI_Turnleft[]   = {0x5A, 0xA5, 0x02, 0x7A, 0x7A, 0x5A};
// 右转
unsigned char UNI_Turnright[]  = {0x5A, 0xA5, 0x02, 0x79, 0x7A, 0x5A};


typedef enum
{
    eUNI_Wake_Up = 0,
    eUNI_BuzzerOn,
    eUNI_BuzzerOff,
    eUNI_LedOn,
    eUNI_LedOff,
    eUNI_Forward,
    eUNI_Stop,
    eUNI_Backward,
    eUNI_Turnleft,
    eUNI_Turnright,
    eUNI_NoMatchCmd,
} eUniCmd;

typedef struct {
    eUniCmd        cmdIndex;
    char*          cmdName;
    unsigned char  cmdLen;
    unsigned char* cmdCode;
} UniCmd;

static UniCmd g_UniCmdList[] = 
{
    {eUNI_Wake_Up,   "Wake_Up",   6, UNI_wakeup_uni},
    {eUNI_BuzzerOn,  "BuzzerOn",  8, UNI_TurnonAlarms},
    {eUNI_BuzzerOff, "BuzzerOff", 8, UNI_TurnoffAlarms},
    {eUNI_LedOn,     "LedOn",     6, UNI_TurnOn},
    {eUNI_LedOff,    "LedOff",    7, UNI_TurnOff},
    {eUNI_Forward,   "Forward",   6, UNI_Forward},
    {eUNI_Stop,      "Stop",      6, UNI_Stop},
    {eUNI_Backward,  "Backward",  6, UNI_Backward},
    {eUNI_Turnleft,  "Turnleft",  6, UNI_Turnleft},
    {eUNI_Turnright, "Turnright", 6, UNI_Turnright},

    {eUNI_NoMatchCmd, "NoMatchCmd", 0, NULL},
};
#define UniCmdCnt (sizeof(g_UniCmdList)/sizeof(g_UniCmdList[0]))

static int g_Uart1Inited = 0;
int Uart1Config(void)
{
    int ret = -1;
    // NOTE: in this case, UART1 only use RxD to receive signal from UNI_SOUND,
    // NOT using TxD to send anything out of Hi3861. GPIO_0 is ctrling LED.
    // IoTGpioInit(HI_IO_NAME_GPIO_0);
    // hi_io_set_func(HI_IO_NAME_GPIO_0, HI_IO_FUNC_GPIO_0_UART1_TXD);

    IoTGpioInit(HI_IO_NAME_GPIO_1);
    hi_io_set_func(HI_IO_NAME_GPIO_1, HI_IO_FUNC_GPIO_1_UART1_RXD);

    IotUartAttribute uart_cfg = {115200, 8, 1, 0, 500, 500, 0};
    
    if (!g_Uart1Inited) {
        ret = IoTUartInit(IOT_UART_IDX_1, &uart_cfg);
        if (ret != 0) {
            printf("%s: IoTUartInit fail\r\n", __func__);
            return -1;
        }
        g_Uart1Inited = 1;
        return 0;
    }
    return 0;
}
void LightConfig(void)
{
    IoTGpioInit(HI_IO_NAME_GPIO_0);
    hi_io_set_func(HI_IO_NAME_GPIO_0, HI_IO_FUNC_GPIO_0_GPIO);
    IoTGpioSetDir(HI_IO_NAME_GPIO_0, IOT_GPIO_DIR_OUT);
}
void BeepConfig(void)
{
    IoTGpioInit(IOT_PWM_BEEP_GPIO);
    hi_io_set_func(IOT_PWM_BEEP_GPIO, HI_IO_FUNC_GPIO_5_PWM2_OUT);
    IoTGpioSetDir(IOT_PWM_BEEP_GPIO, IOT_GPIO_DIR_OUT);
    IoTPwmInit(IOT_PWM_PORT_PWM2);
}
static eUniCmd FindMatchCmdId(unsigned char *RemoteArray, unsigned char RemoteLen)
{
    int totalCmdCnt = UniCmdCnt;
    int i = 0;
    int j = 0;
    if ((UNI_CMD_LEN_MIN <= RemoteLen) && (RemoteLen <= UNI_CMD_LEN_MAX)) {
        for(i = 0; i < totalCmdCnt; i++ ) {
            if (g_UniCmdList[i].cmdLen == RemoteLen) {
                for(j = 0; j < RemoteLen; j++) {
                    if(RemoteArray[j] != g_UniCmdList[i].cmdCode[j]) {
                        break;  // g_UniCmdList[i] is NOT match with RemoteArray[]
                    }
                }
                if (j == RemoteLen) {  // found the match cmd
                    return g_UniCmdList[i].cmdIndex;
                }
            }
        }
    } 
    return g_UniCmdList[totalCmdCnt-1].cmdIndex;  //NoMatchCmd
}

void ProcessRecvCmd(eUniCmd eCmd)
{
    switch (eCmd) {
    case eUNI_Wake_Up:
        g_carInfo.e_CM = eMode_AudCmd;
        g_carInfo.e_ST = eSt_Stop;
        page_curr = 2;  // 任何状态下语音唤醒大师兄，都会进入语音控制模式，等待命令
        break;
    case eUNI_BuzzerOn:
        IoTPwmStart(IOT_PWM_PORT_PWM2, IOT_PWM_BEEP_DUTY, IOT_PWM_BEEP_FREQ);
        break;
    case eUNI_BuzzerOff:
        IoTPwmStop(IOT_PWM_PORT_PWM2);
        break;
    case eUNI_LedOn:
        IoTGpioSetOutputVal(HI_IO_NAME_GPIO_0, 1);
        break;
    case eUNI_LedOff:
        IoTGpioSetOutputVal(HI_IO_NAME_GPIO_0, 0);
        break;
    case eUNI_Forward:
        if (g_carInfo.e_CM == eMode_AudCmd) {
            g_carInfo.e_ST = eSt_Forward;
        }
        break;
    case eUNI_Stop:
        if (g_carInfo.e_CM == eMode_AudCmd) {
            g_carInfo.e_ST = eSt_Stop;
        }
        break;
    case eUNI_Backward:
        if (g_carInfo.e_CM == eMode_AudCmd) {
            g_carInfo.e_ST = eSt_Backward;
        }
        break;
    case eUNI_Turnleft:
        if (g_carInfo.e_CM == eMode_AudCmd) {
            g_carInfo.e_ST = eSt_TurnLeft;
        }
        break;
    case eUNI_Turnright:
        if (g_carInfo.e_CM == eMode_AudCmd) {
            g_carInfo.e_ST = eSt_TurnRight;
        }
        break;
    case eUNI_NoMatchCmd:
    default:
        break;
    }
}

#define BUFF_SIZE  (16)
void UnisoundTask(void)
{
    unsigned int len = 0;
    unsigned char uartReadBuff[BUFF_SIZE] = {0};
    eUniCmd matchCmd;

    printf("%s: Begin:\n", __func__);
    LightConfig();
    BeepConfig();
    if (Uart1Config() != 0) {
        printf("%s: Uart1Config() fail\r\n", __func__);
        return;
    }

    while (1) {
        memset(uartReadBuff, 0, BUFF_SIZE);
        len = IoTUartRead(IOT_UART_IDX_1, uartReadBuff, BUFF_SIZE);
        if ((UNI_CMD_LEN_MIN <= len) && (len <= UNI_CMD_LEN_MAX)) {
            matchCmd = FindMatchCmdId(uartReadBuff, len);
            printf("%s: get cmd: %s\n", __func__, g_UniCmdList[matchCmd].cmdName);

            ProcessRecvCmd(matchCmd);
            if (matchCmd < eUNI_NoMatchCmd) {
                usleep(1000*1000);
            }
        }
        usleep(100*1000);
    }
    printf("%s: End.\n", __func__);

    return;
}

osThreadId_t UnisoundTaskEntry(void)
{
    //osPriorityAboveNormal[32], osPriorityNormal[24]
    //{.name, .attr_bits, .cb_mem, .cb_size, .stack_mem, .stack_size, .priority, .tz_module, .reserved}
    osThreadAttr_t attr = {"UnisoundTask", 0, NULL, 0, NULL, 1024*4, 24, 0, 0};
    osThreadId_t UniTaskId = osThreadNew((osThreadFunc_t)UnisoundTask, NULL, &attr);
    if (UniTaskId == NULL) {
        printf("[UnisoundTaskEntry] Failed to create %s!\n", attr.name);
    }
    return UniTaskId;
}
